#ifndef NETWORKPING_H_
#define NETWORKPING_H_

#include <QDebug>
#include <QThread>
#include <stdint.h>

#ifdef WIN32
#include "WinSock2.h"

#define socklen_t int
#else
#include "sys/socket.h"
#include "netinet/in.h"
#include "errno.h"
#include "arpa/inet.h"
#include "unistd.h"
#include "netdb.h"
#include "sys/time.h"

#define SOCKET_ERROR -1
#define Sleep(ms) usleep(ms * 1000);

typedef unsigned long ULONG;
typedef unsigned short USHORT;
typedef unsigned char UCHAR;
typedef long long LONGLONG;
#endif

#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST    8
#define ECHO_REPLY     0

enum NetworkPingState
{
    NETWORK_INIT_ERROR,
    NETWORK_NAME_ERROR,
    NETWORK_PING_ERROR,
    NETWORK_PING_OK
};

class NetworkPing : public QThread
{
    Q_OBJECT
private:
    struct IPHeader
    {
        uint8_t     u8_VerHLen;
        uint8_t     u8_TOS;
        uint16_t    u16_TotalLen;
        uint16_t    u16_TD;
        uint16_t    u16_flagFragOffset;
        uint8_t     u8_TTL;
        uint8_t     u8_protocol;
        uint16_t    u16_checksum;
        uint32_t    u32_srcIP;
        uint32_t    u32_destIP;
    };

    struct ICMPHeader
    {
        uint8_t     u8_type;
        uint8_t     u8_code;
        uint16_t    u16_checkSum;
        uint16_t    u16_ID;
        uint16_t    u16_seq;
        uint32_t    u32_timeStamp;
    };

    struct PingReply
    {
        uint16_t    u16_seq;
        uint32_t    u32_roundTripTime;
        uint32_t    u32_bytes;
        uint32_t    u32_TTL;
    };

public:
    NetworkPing();
    ~NetworkPing();

    void ping(const QList<std::string> list, uint32_t timeOut = 2000);
    NetworkPingState syncPing(const std::string &url, uint32_t timeOut = 2000);

    void exitPing();

signals:
    void pingState(const NetworkPingState &mask);

protected:
    void run() override;

private:
    uint8_t getSendCount() const;
    uint8_t getReceiveCount() const;
    uint8_t getPacketLossRate() const;
    uint32_t getPacketLossBytes() const;
    uint32_t getShortestTime() const;
    uint32_t getLongestTime() const;
    uint32_t getAvgTime() const;

    bool privatePing(char *destIP, PingReply *pPingReply, uint32_t u32_timeOut);
    bool privatePingCore(uint32_t u32_destIP, PingReply *pPingReply, uint32_t u32_timeOut);
    bool initSocket();
    uint16_t calcCheckSum(uint16_t *pBuffer, int size);
    uint32_t getCurrentTime();

#ifdef WIN32
    SOCKET     sockfd;
#else
    int        sockfd = 0;
#endif
    bool m_isQuit = false;

    uint16_t   u16_currentProcID;
    char       *ICMPData = nullptr;
    uint8_t    sendCount = 0;
    uint64_t    receiveCount = 0;
    uint8_t    packetLossRate = 0;
    uint32_t   shortestTime = 2000;
    uint32_t   longestTime = 0;
    uint32_t   avgTime = 0;
    uint32_t   totalTime = 0;
    uint32_t   packetLossBytes = 0;
    uint16_t   u16_packetSeq;
    QList<std::string> urls;
    int m_index = 0;
    uint32_t   timeOut;
};

#endif // NETWORKPING_H_
